/*
 * @(#)XfdmEvaluation.java        1.0 2009/01/01
 *
 * This file is part of Xfuzzy 3, a design environment for fuzzy logic
 * based systems.
 *
 * (c) 2000 IMSE-CNM. The authors may be contacted by the email address:
 *                    xfuzzy-team@imse.cnm.es
 *
 * Xfuzzy is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation.
 *
 * Xfuzzy is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 */

package xfuzzy.xfdm.model;

import xfuzzy.lang.*;
import xfuzzy.xfds.XfdsDataSet;
import java.io.*;

/**
 * Clase que evalua un sistema
 * 
 * @author Francisco Jos� Moreno Velo
 *
 */
public class XfdmEvaluation 
{	
	//----------------------------------------------------------------------------//
	//                             M�TODOS P�BLICOS                               //
	//----------------------------------------------------------------------------//

	/**
	 * Eval�a el sistema
	 */
	public static void evaluate(Specification spec, XfdsDataSet training, XfdsDataSet test, File log) 
	{
		try 
		{
			FileOutputStream fos = new FileOutputStream(log,true);
			PrintStream stream = new PrintStream(fos);
			describeSpecification(spec,stream);
			if(training.isAClassification()) 
			{
				evalClassification(spec, training, test, stream);
			}
			
			if(training.isARegression()) 
			{
				evalRegression(spec, training, test, stream);
			}
		}
		catch(Exception ex)
		{
		}
	
	}

	//----------------------------------------------------------------------------//
	//                             M�TODOS PRIVADOS                               //
	//----------------------------------------------------------------------------//

	/**
	 * Descripci�n del sistema
	 */
	private static void describeSpecification(Specification spec, PrintStream stream)
	{
		SystemModule system = spec.getSystemModule();
		RuleSetCall[] call = system.getRulebaseCalls();
		KnowledgeBase base = call[0].getRulebase();
		int rules = base.getNumOfRules();
		
		stream.println("Number of rules: "+rules);
	}
	
	/**
	 * Evaluaci�n del resultado
	 */
	private static void evalClassification(Specification spec
			, XfdsDataSet training
			, XfdsDataSet test
			, PrintStream stream) 
	{
		SystemModule system = spec.getSystemModule();
		int num_outputs = training.getNumberOfOutputs();
		
		int trn_missc = 0;
		double trn_missc_rate = 0;
		int test_missc = 0;
		double test_missc_rate = 0;
		
		for(int p=0; p<training.input.length; p++) 
		{
			double[] output = system.crispInference(training.input[p]);
			double err = 0;
			for(int i=0; i<output.length; i++) 
			{
				if(output[i] != training.output[p][i]) err++;
			}
			trn_missc += err;
		}
		trn_missc_rate = (1.0 * trn_missc) / (num_outputs * training.input.length);
		
		if(test != null) 
		{		
			for(int p=0; p<test.input.length; p++) 
			{
				double[] output = system.crispInference(test.input[p]);
				double err = 0;
				for(int i=0; i<output.length; i++) 
				{
					if(output[i] != test.output[p][i]) err++;
				}
				test_missc += err;
			}
			test_missc_rate = (1.0 * test_missc) / (num_outputs * test.input.length);
		}
		
		stream.println("Evaluation:");
		stream.println("    (Training) Missclassified instances: "+trn_missc);
		stream.println("    (Training) Missclassification rate: "+trn_missc_rate);
		stream.println("    (Test) Missclassified instances: "+test_missc);
		stream.println("    (Test) Missclassification rate: "+test_missc_rate);
	}
	
	/**
	 * Evaluaci�n del proceso de regresi�n
	 */
	private static void evalRegression(Specification spec
			, XfdsDataSet training
			, XfdsDataSet test
			, PrintStream stream) 
	{
		SystemModule system = spec.getSystemModule();
		double trn_mxae=0;
		double trn_mse=0;
		for(int p=0; p<training.input.length; p++) 
		{
			double[] output = system.crispInference(training.input[p]);
			for(int i=0; i<output.length; i++) 
			{
				double dev = (output[i]-training.output[p][i])/training.range[i];
				if(dev<0) dev = -dev;
				trn_mse += dev*dev/output.length;
				if(dev>trn_mxae) trn_mxae = dev;
			}
		}
				
		double test_mxae=0;
		double test_mse=0; 
		if(test != null) 
		{
			for(int p=0; p<test.input.length; p++) 
			{
				double[] output = system.crispInference(test.input[p]);
				for(int i=0; i<output.length; i++) 
				{
					double dev = (output[i]-test.output[p][i])/test.range[i];
					if(dev<0) dev = -dev;
					test_mse += dev*dev/output.length;
					if(dev>test_mxae) test_mxae = dev;
				}
			}
		}
		
		stream.println("Evaluation:");
		stream.println("    (Training) Mean Square Error: "+trn_mse);
		stream.println("    (Training) Root Mean Square Error: "+Math.sqrt(trn_mse));
		stream.println("    (Training) Maximum Absolute Error: "+trn_mxae);
		stream.println("    (Test) Mean Square Error: "+test_mse);
		stream.println("    (Test) Root Mean Square Error: "+Math.sqrt(test_mse));
		stream.println("    (Test) Maximum Absolute Error: "+test_mxae);
	}
}
